package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.pojo.jsonResult.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.jsonResult.vo.ChargingProcessVO;
import cn.tedu.charging.cost.dao.repository.CostRepository;
import cn.tedu.charging.cost.pojo.ChargingData;
import cn.tedu.charging.cost.pojo.po.ChargingDataPO;
import cn.tedu.charging.cost.pojo.po.CostRulePO;
import cn.tedu.charging.cost.dao.repository.ChargingDataESRepository;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
public class CostServiceImpl implements CostService {

    @Autowired
    private CostRepository costRepository;
    /*ES 的service接口*/
    @Autowired
    private ChargingDataESRepository chargingDataESRepository;

    /**
     * 全局的map 用来存储设备同步的充电数据
     * Key 订单号 value 充电数据
     */
    private Map<String, ChargingData> chargingDataMap = new HashMap<>();

    private ChargingDataPO getChargingDataByOrderNoFromES(String orderNo){
        Optional<ChargingDataPO> optional = chargingDataESRepository.findById(orderNo);
        if (optional.isPresent()){
            //optional.get()获取到Optional对象中的值  存在
            //ES有数据，不是第一次存
            ChargingDataPO chargingDataPO = optional.get();
            Integer count = chargingDataPO.getCount();
            count=count+1;
            chargingDataPO.setCount(count);
            log.debug("第{}次同步数据，保存数据到ES:{}",count,chargingDataPO);
            chargingDataESRepository.save(chargingDataPO);
            log.debug("第{}次同步数据，保存数据到ES成功:{}",count,chargingDataPO);
            return chargingDataPO;
        }else{
            //ES没有数据，是第一次存
            ChargingDataPO chargingDataPO=new ChargingDataPO();
            chargingDataPO.setId(orderNo);
            Integer count = 1;
            chargingDataPO.setCount(count);
            log.debug("第{}次同步数据， 保存数据到ES:{}",count,chargingDataPO);
            chargingDataESRepository.save(chargingDataPO);
            log.debug("第{}次同步数据， 保存数据到ES成功:{}",count,chargingDataPO);
            return chargingDataPO;

        }
    }
    /**
     * 通过订单号查询充电数据
     * @param orderNo

     * @return
     */
    private  ChargingData getChargingDataByOrderNo(String orderNo){
        ChargingData chargingData = chargingDataMap.get(orderNo);
        if (chargingData == null) {
            //等于null 表示是第一次同步充电数据
            //创建充电数据对象
            chargingData = new ChargingData();
            chargingData.setOrderNo(orderNo);
            chargingData.setCount(1);
            //保存到map中 为下次同步做准备
            chargingDataMap.put(orderNo,chargingData);
        }else {
            //不等于null 表示不是第一次同步充电数据 可能是第二次 也可能是第N次
            //获取上次的同步次数
            Integer count = chargingData.getCount();
            //上次的次数 + 本次  = 总次数
            count = count + 1;
            //保存本次次数 为下次计算做准备
            chargingData.setCount(count);

/*假数据代码*/
//            //获取上次的花费
//            BigDecimal lastTotalFee = chargingData.getTotalFee();
//            //上次的花费 + 本次话费  = 总话费
//            lastTotalFee = lastTotalFee.add(totalFee);
//            //保存本次花费 为下次计算做准备
//            chargingData.setTotalFee(lastTotalFee);
//
//            //获取上次的充电度数
//            Float lastChargingCapacity = chargingData.getChargingCapacity();
//            //实际充电度数 = 本次充电度数 - 上次充电度数
//            Float realChargingCapacity = chargingCapacity - lastChargingCapacity;
//            log.debug("本次实际充电度数:{}",realChargingCapacity);
//            //保存本次充电度数 为下次计算做准备
//            chargingData.setChargingCapacity(chargingCapacity);

            //保存到map中 为下次同步做准备 ???
            //chargingDataMap.put(orderNo,chargingData);

        }
        return chargingData;
    }

    @Override
    public ChargingProcessVO calculate(ChargingProcessParam chargingProcessParam) {

/**
 * 之前写的demo代码定义的假数据
 */
//        ChargingData chargingData = getChargingDataByOrderNo(chargingProcessParam.getOrderNo(),
//                chargingProcessParam.getChargingCapacity(), new BigDecimal(10));
//        log.debug("通过订单号:{},获取充电数据:{}",chargingProcessParam.getOrderNo(),
//                chargingData);


        Integer currentHour = getCurrentHour();
        log.debug("获取当前时间小时数:{}",currentHour);
        Integer gunType = chargingProcessParam.getGunType();
        Integer stationId = chargingProcessParam.getStationId();
        CostRulePO costRulePO = getMatchCostRuleByGunTypeAndTime(
                stationId,
                gunType,
                currentHour);
        log.debug("通过场站id:{},枪类型:{},时间:{},匹配到计价规则:{}",
                stationId,gunType,currentHour,costRulePO);

        if (costRulePO != null) {
            //获取到匹配的计价规则的名称
            String ruleName = costRulePO.getName();
            //获取到匹配的计价规则的每度电的价格
            BigDecimal powerFee = costRulePO.getPowerFee();
            //获取设备同步的充电度数
            Float chargingCapacity = chargingProcessParam.getChargingCapacity();
            //通过订单号获取到充电数据
            //ChargingData chargingData = getChargingDataByOrderNo(chargingProcessParam.getOrderNo());
            ChargingDataPO chargingDataPO =
                    getChargingDataByOrderNoFromES(chargingProcessParam.getOrderNo());

/*
            //判断是否是第一次保存充电数据
            if (chargingData.getCount() == 1) {
                //是第一次保存充电数据
                firstCost2ChargingData(chargingData,
                        chargingCapacity,
                        powerFee,
                        currentHour,
                        ruleName);

            }else{
                //是第N次保存充电数据
                lastCost2ChargingData(chargingData,
                        chargingCapacity,
                        powerFee,
                        currentHour,
                        ruleName);
            }

 */
            cost2ChargingData(chargingDataPO,chargingCapacity,powerFee,currentHour,ruleName);
            return buildChargingProcessVO(chargingDataPO);
        }else {
            log.error("故障，计价规则为空，进行系统告警，业务停止");
        }

        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        chargingProcessVO.setOrderNo(chargingProcessParam.getOrderNo());
        chargingProcessVO.setGunId(chargingProcessParam.getGunId());
        return chargingProcessVO;
    }

    /**
     * 过充电数据 构建 计价服务 价格计算的出参
     * @param chargingData
     * @return
     */
    private ChargingProcessVO buildChargingProcessVO(ChargingDataPO chargingData) {
        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        chargingProcessVO.setOrderNo(chargingData.getOrderNo());
       // chargingProcessVO.setGunId(chargingData.getGunId());
        chargingProcessVO.setTotalFee(chargingData.getTotalFee());
        chargingProcessVO.setChargingCapacity(chargingData.getChargingCapacity());

        //如果参数过多，可以通过BeanUtils工具类进行拷贝
        BeanUtils.copyProperties(chargingData,chargingProcessVO);

        //todo 自己实现 去 用时间的API去实现
        //充电总时长的计算  充电的结束时间 - 充电的开始时间
        //设备第一次同步的时候 记录下时间
        //充电完成后 获取当前时间 - 记录的开始时间 = 充电花费的总时长
        //chargingProcessVO.setChargingTime();

        return chargingProcessVO;

    }

    private void cost2ChargingData(ChargingDataPO chargingData,
                                   Float chargingCapacity,BigDecimal powerFee,
                                   Integer currentHour,String ruleName){
        log.debug("计算价格");
        //获取实际充电度数
        Float realChargingCapacity =
                chargingData.getChargingCapacity(chargingCapacity);
        //计算价格 充电度数 * 每度电的价格
        BigDecimal cost = getCost(realChargingCapacity, powerFee);
        //保存度数 到 chargingData 为了下一次计算 <<实际充电度数>> 做准备
        chargingData.setChargingCapacity(chargingCapacity);
        //保存 本次花费 到 chargingData 为了下一次计算 <<总花费>> 做准备
        chargingData.setTotalFee(cost);
        log.debug("同步数据，" +
                        "当前时间:{},计价规则名称:{}," +
                        "充电度数:{},电费单价:{}," +
                        "本次花费:{},总花费:{}",
                currentHour,ruleName,
                chargingCapacity,powerFee,
                cost,chargingData.getTotalFee());

        //注意 用ES 保存数据 修改完 chargingDataPO 一定别忘了保存 save
        log.debug("第{}次同步数据，  保存数据到ES:{}",chargingData.getCount(),chargingData);
        chargingDataESRepository.save(chargingData);
        log.debug("第{}次同步数据，  保存数据到ES成功:{}",chargingData.getCount(),chargingData);

    }

    /**
     * 计算第一次花费 将数据存到chargingData中
     *
     * 第一次
     *       本次花费  = 计价规则的电费 * 充电度数
     *       总花费 = 本次花费
     */
    private void firstCost2ChargingData(ChargingData chargingData,
                                        Float chargingCapacity,
                                        BigDecimal powerFee,
                                        Integer currentHour,
                                        String ruleName){
        log.debug("第一次同步数据");
        //本次花费
        BigDecimal cost=getCost(chargingCapacity,powerFee);
        //度数  保存到chargingData中
        chargingData.setChargingCapacity(chargingCapacity);
        //本次花费 保存到chargingData中
        chargingData.setTotalFee(cost);
        log.debug("第一次同步数据，" +
                        "当前时间:{},计价规则名称:{}," +
                        "充电度数:{},电费单价:{}," +
                        "本次花费:{},总花费:{}",
                currentHour,ruleName,
                chargingCapacity,powerFee,
                cost,cost);
    }

    /**
     *第N次
     * 本次花费  = 计价规则的电费 * （本次充电度数 - 上次充电度数 = 实际充电度数）
     * 总花费 = 本次花费 + 上次话费
     */
    private void lastCost2ChargingData(ChargingData chargingData,
                                       Float chargingCapacity,
                                       BigDecimal powerFee,
                                       Integer currentHour,
                                       String ruleName){
        log.debug("第N次同步数据");
        //第N次充电记录的实际度数
        Float realChargingCapacity = chargingCapacity-chargingData.getChargingCapacity();
        //计算本次实际充电度数的花费
        BigDecimal cost = getCost(realChargingCapacity, powerFee);
        //保存本次充电度数到chargingData中
        chargingData.setChargingCapacity(chargingCapacity);
        //总花费
        BigDecimal totalFee = chargingData.getTotalFee().add(cost);
        //保存总花费到chargingData中 为下一次同步做准备
        chargingData.setTotalFee(totalFee);
        log.debug("第N次同步数据，" +
                        "当前时间:{},计价规则名称:{}," +
                        "充电度数:{},电费单价:{}," +
                        "本次花费:{},总花费:{}",
                currentHour,ruleName,
                chargingCapacity,powerFee,
                cost,totalFee);
    }






    private BigDecimal getCost(Float chargingCapacity, BigDecimal powerFee) {
        BigDecimal bigDecimal = BigDecimal.valueOf(chargingCapacity);
        //BigDecimal add 加计算  multiply 乘法
        return bigDecimal.multiply(powerFee);
    }

    private CostRulePO getMatchCostRuleByGunTypeAndTime(Integer stationId,
                                                        Integer gunType,
                                                        Integer hour) {
        log.debug("通过场站id获取计价规则列表-入参:{}",stationId);
        List<CostRulePO> costRulePOS = getCostRuleByStationId(stationId);
        log.debug("通过场站id获取计价规则列表-入参:{},出参:{}",stationId,costRulePOS);

        if (!CollectionUtils.isEmpty(costRulePOS)) {
            for (CostRulePO costRulePO : costRulePOS) {
                //获取计价规则的枪类型
                Integer costRuleGunType = costRulePO.getGunType();
                //判断入参的枪类型和计价规则的枪类型是否一致
                if (costRuleGunType.equals(gunType)){
                    //获取计价规则开始时间
                    Integer startTime = costRulePO.getStartTime();
                    //获取计价规则结束时间
                    Integer endTime = costRulePO.getEndTime();
                    if (hour >= startTime && hour < endTime) {
                        return costRulePO;
                    }
                }
            }
        }
        //循环完了，还没有找到匹配的，返回null，表示没有匹配到计价规则
        return null;
    }

    /**
     * 通过场站id查询 当前场站所有的计价规则
     * @param stationId
     * @return
     */
    private List<CostRulePO> getCostRuleByStationId(Integer stationId) {
        return costRepository.getCostRulesByStationId(stationId);
    }


    /**
     * 获取当前时间的小时
     * @return
     */
    private Integer getCurrentHour() {
        LocalDateTime localDateTime = LocalDateTime.now();
        return localDateTime.getHour();
    }
}
